<?php

namespace App\Kits\Middleware;

use App\Kits\Errors\BaseException;
use App\Kits\Errors\TokenInvalidException;
use App\Kits\Repository\LibTokenRepository;
use App\Kits\ResponseCode;
use Illuminate\Auth\AuthManager;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Passport\PersonalAccessTokenResult;

/**
 * passport jwt token验证中间件
 * Class JwtTokenMiddleware
 * @package App\Kits\Middleware
 */
class JwtTokenMiddleware
{
    protected $auth;
    protected $tokenRepository;

    public function __construct(AuthManager $auth, LibTokenRepository $tokenRepository)
    {
        $this->auth = $auth;
        $this->tokenRepository = $tokenRepository;

    }

    /**
     * passport jwt token验证和过期刷新
     * @param Request $request
     * @param callable $next
     * @param string $guard
     * @return mixed
     * @throws \Exception
     */
    public function handle(Request $request, callable $next, string $guard='api')
    {
        try {
            // 设置当前请求的默认guard
            $this->auth->shouldUse($guard);
            if (!$this->auth->guard($guard)->check()) { // Token无效
                if (!$this->tokenRepository->isRefreshExpired()) { // Token不可进行刷新或未设置
                    $this->tokenRepository->setTokenRevoked();
                    throw new TokenInvalidException('无效登录信息,请重新登录');
                }
                $userProvider = $this->auth->createUserProvider($guard);
                if (is_null($userProvider)) {
                    throw new TokenInvalidException('无效登录信息,请重新登录');
                }
                $user = $this->tokenRepository->getUser($userProvider->createModel());
                if (!$user) { // 用户不存在
                    $this->tokenRepository->setTokenRevoked();
                    throw new TokenInvalidException('登录信息已过期,请重新登录');
                }
                $oldToken = $this->tokenRepository->getTokenEntity();
                if (!$oldToken) { // 数据库中的token已被删除（正常操作不会出现）
                    throw new TokenInvalidException('登录信息已过期,请重新登录');
                }
                if ($oldToken->revoked) { // 已主动设为过期
                    if ($oldToken->expires_at < new \DateTime()) { // Token已过期
                        $oldToken->delete();
                    }
                    throw new TokenInvalidException('登录信息已过期,请重新登录');
                }
                // 生成新的token
                /** @var PersonalAccessTokenResult $tokenResult */
                $tokenResult = $user->createToken($oldToken->name, $oldToken->scopes);

                $request->setStatusCode = ResponseCode::CODE_206;
                if (!$request->withHeaders) {
                    $request->withHeaders = [];
                }
                $request->withHeaders = array_merge($request->withHeaders, ['token' => $tokenResult->accessToken]);
                // 使旧的token过期
                $this->tokenRepository->setTokenRevoked();
                // 使当前请求有效
                $this->auth->guard($guard)->setUser($user);
                $user->withAccessToken($tokenResult->token); // 刷新当前请求的新的AaccessToken
            }
            return $next($request);
        } catch (\Exception $exception) {
            return BaseException::exceptionHandle($request, $exception);
        }


    }

    /**
     * 设置Response额外的数据（例：刷新token）
     * @param Request $request
     * @param $response
     * @return Response
     */
    public static function transformResponse(Request $request, $response)
    {

        if ($request->setStatusCode) {
            $response->setStatusCode($request->setStatusCode);
        }
        if (is_array($request->withHeaders)) {
            $response->withHeaders($request->withHeaders);
        }
        return $response;
    }
}
